package com.zengdw.pluginhost;

import com.zengdw.easytext.api.Analyzer;

import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: zengd
 * @Date: 2020/7/24 9:46
 * @Version: 1.0
 */
public class Main {
    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println("Please provide plugin directories");
            return;
        }
        System.out.println("Loading plugins from " + Arrays.toString(args));

        Stream
                .of(args)
                //针对参数提供的每一个目录，都会在createPluginlayer中实例化一个 Module Layer
                .map(Main::createPluginlayer)
                //Service Loader::load 调用是以每一层作为参数执行的，并从该层返回插件服务。
                .flatMap(moduleLayer -> ServiceLoader.load(moduleLayer, Analyzer.class).stream()
                        .filter(provider -> provider.get().getName().startsWith("plugin")))
                .forEach(analyzerProvider -> {
                    System.out.println("Invoking " + analyzerProvider.get().getName());
                    //在所有插件上调用 analyze 方站
                    double analyze = analyzerProvider.get().analyze();
                    System.out.println(analyze);
                });
    }

    private static ModuleLayer createPluginlayer(String dir) {
        ModuleFinder finder = ModuleFinder.of(Paths.get(dir));
        Set<ModuleReference> pluginModuleRefs = finder.findAll();
        Set<String> plugins = pluginModuleRefs.stream()
                .map(ref -> ref.descriptor().name())
                //.filter(name -> name.startsWith("plugin"))
                .collect(Collectors.toSet());

        ModuleLayer parent = ModuleLayer.boot();
        Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), plugins);

        ClassLoader scl = ClassLoader.getSystemClassLoader();
        //插件层中的所有模块将使用相同的类加载器来定义。
        return parent.defineModulesWithOneLoader(cf, scl);
    }
}
